/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.statemachine.command;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.commands.Command;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Region;
import org.eclipse.uml2.uml.State;
import org.eclipse.uml2.uml.StateMachine;

import com.cea.papyrus.core.commands.UMLElementCreateCommand;
import com.cea.papyrus.core.helper.NodeHelper;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.diagraminterchange2.di2.Di2Factory;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Property;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * A command to specifically create regions (uml elements and associated graph nodes)
 *  
 * @author David Servat
 * @since 9 mar. 07
 */
public class CreateRegionCommand extends Command {
	/** Debug channel. */
	private int channel = 501;

	/** The region to add. */
	private GraphNode childRegion = null;
	
	/** The state machine. */
	private StateMachine stateMachine = null;
	
	/** The state. */
	private State state = null;
	
	/** The parent, i.e. either the region where we want to add a new one or the state. */
	private GraphNode parent = null;
	
	/** The location. */
	private Point location = null;

	/** Inner command for the creation of uml element. */
	private UMLElementCreateCommand umlElementCreateCommand = null;

	/** Inner command for the addition of the child region on the parent region. */
	private AddRegionNodeCommand addRegionNodeCommand = null;
	
	/** Flag to handle the creation of the first Region of a State. */
	private boolean createFirstRegionOfState = false;
	
	/**
	 * The constructor.
	 * 
	 * @param child region to add
	 * @param parent region where we add
	 */
	public CreateRegionCommand(GraphElement parent, Point location){
		Debug.debug(this,"Call CreateRegionCommand(GraphElement parent, Point location, Dimension size)", channel); //$NON-NLS-1$
		
		//a bunch of precautionary tests
		if(parent == null)
			throw new IllegalArgumentException();

		if(parent.getSemanticModel() == null)
			throw new IllegalArgumentException();

		Element elt = ((Uml1SemanticModelBridge)parent.getSemanticModel()).getElement();
		if(elt == null)
			throw new IllegalArgumentException();

		if(!((elt instanceof Region) || (elt instanceof State)))
			throw new IllegalArgumentException();
		
		//now we can store the parameters
		this.parent = (GraphNode)parent;
		this.location = location;
		if(elt instanceof State){
			createFirstRegionOfState = true;
			state = (State)elt;
		}
		else if(elt instanceof Region){
			Element semElt = (Element)((Uml1SemanticModelBridge)((GraphNode)parent.getSemanticParent()).getSemanticModel()).getElement();
			if(semElt instanceof StateMachine)
				stateMachine = (StateMachine)semElt;
			else if(semElt instanceof State)
				state = (State)semElt;
		}
	}

	/**
	 * Does the job of adding a child region on a parent region and uml element creation
	 * by way of an internal UMLElementCreateCommand
	 * 
	 */
	@Override
	public void execute(){
		Debug.debug(this, "call CreateRegionCommand.execute()", channel);
		
		if(stateMachine != null)
			umlElementCreateCommand = new UMLElementCreateCommand(Region.class, stateMachine);
		else if(state != null)
			umlElementCreateCommand = new UMLElementCreateCommand(Region.class, state);			
		umlElementCreateCommand.execute();
		
		Region region = (Region)umlElementCreateCommand.getElement();
		
		childRegion = new NodeHelper().createGraphElement(region, null, location, null);

		//assign default values for zone and dropLocation properties
		Property prop = Di2Factory.eINSTANCE.createProperty();
		prop.setKey("zone");
		prop.setValue("");
		childRegion.getProperty().add(prop);

		prop = Di2Factory.eINSTANCE.createProperty();
		prop.setKey("dropLocation");
		prop.setValue("");
		childRegion.getProperty().add(prop);
		
		GraphNode labelNode = new NodeHelper().createGraphElement(null, null, new Point(0, 0), null);
		labelNode.setSemanticParent(childRegion);
	    labelNode.setContainer(childRegion);
	    labelNode.setIsVisible(false);
	    
	    //in the general case, i.e. creating a Region on a Region
		//the job is delegated to a specific command
	    if(!createFirstRegionOfState){
			addRegionNodeCommand = new AddRegionNodeCommand(childRegion, parent, location);
			addRegionNodeCommand.execute();
	    }
	    else{
			int defaultY = 20;
			childRegion.setPosition(new Point(0, defaultY));
			Dimension parentSize = parent.getSize();
	    	childRegion.setSize(new Dimension(parentSize.width, parentSize.height-defaultY));
	    	childRegion.setSemanticParent(parent);
	    	parent.getContained().add(childRegion);
	    }
	}
	
	/** 
	 * Performs the redo() method.
	 */
	@Override
	public void redo(){
		Debug.debug(this, "call CreateRegionCommand.redo()", channel);
		
		umlElementCreateCommand.redo();
		if(!createFirstRegionOfState)
			addRegionNodeCommand.redo();
		else{
			childRegion.setSemanticParent(parent);
			parent.getContained().add(childRegion);
		}
	}


	/**
	 * Performs the undo.
	 * 
	 */
	public void undo(){
		Debug.debug(this, "call CreateRegionCommand.undo()", channel);
		
		if(!createFirstRegionOfState)
			addRegionNodeCommand.undo();
		else{
			childRegion.setSemanticParent(null);
			parent.getContained().remove(childRegion);
		}
		umlElementCreateCommand.undo();
	}
}
